Skip to content

feat(export-package): enforce complete game asset packages - #97

Open
xyh202131 wants to merge 4 commits into
1024XEngineer:mainfrom
xyh202131:feat/export-package
Open

feat(export-package): enforce complete game asset packages#97
xyh202131 wants to merge 4 commits into
1024XEngineer:mainfrom
xyh202131:feat/export-package

Conversation

@xyh202131

@xyh202131 xyh202131 commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

本次更新

  • 导出范围收敛为逐帧原图、按动作和方向生成的 Sprite Sheet、动画 JSON。
  • 删除 GIF 编码实现、类型声明和 gifenc 依赖,避免引入未确认的交付格式。
  • 保留帧尺寸、动作方向、时长和位移元数据,供游戏侧直接消费。
  • 导出前继续校验资源完整性,缺帧或尺寸不一致时明确失败。

验证

  • TypeScript 类型检查通过。
  • oxlint 通过。
  • 全量前端测试:26 files / 157 tests 通过。
  • 生产构建通过。

@vercel

vercel Bot commented Aug 4, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
windup Ready Ready Preview Aug 8, 2026 11:51am

@xyh202131 xyh202131 changed the title feat(export-package): add game asset packaging feat(export-package): enforce complete game asset packages Aug 4, 2026
@xyh202131
xyh202131 marked this pull request as ready for review August 4, 2026 06:45

@fennoai fennoai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two concrete issues found in the new export flow.

The archive root/filename is keyed only by character, and the PNG loader rejects valid PNGs when the response Blob has no image/png MIME type.

return safeSegment(id, 'id').slice(-8) || 'id'
}

function packageRoot(model: ExportPackageModel): string {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

High: this root omits outfitId/outfitName, even though the app routes exports by characterId and outfitId. Two outfits for the same character will generate the same ZIP name and internal folder root, so one export can overwrite or mask another on disk. Include the outfit identifier in the package root or filename.

const reason = error instanceof Error ? error.message : '未知错误'
throw new Error(`${field}: 图片读取失败(${reason})`)
}
if (blob.type.toLowerCase() !== 'image/png') throw new Error(`${field}: 文件类型必须是 image/png`)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Medium: this hard MIME check rejects valid PNG bytes whenever the response Blob has an empty or generic type (common when the server omits Content-Type). The code already validates the PNG signature and alpha channel from the bytes, so this extra gate can fail legitimate exports unnecessarily.

@johnnyzhang-eng

Copy link
Copy Markdown

#119 一起合并时构建失败:缺 ajv 依赖 + Frame.rootMotion 字段归属未定

main + #117 + #119 + #111 + #110 合成集成分支后再合本 PR,tsc -b 报两条(原样贴):

src/features/export-package/asset-export.test.ts(2,21): error TS2307: Cannot find module 'ajv/dist/2020.js' or its corresponding type declarations.
src/features/export-package/model.ts(10,21): error TS2339: Property 'rootMotion' does not exist on type 'Frame'.

两条性质不同:

第一条是依赖漏声明 —— ajv 没进 package.json,补上即可。

第二条是两个 PR 之间的真实契约不一致,不是合并冲突 —— 本 PR 假设 Frame 上有 rootMotion,而 #119 定义的 Frame 类型没有这个字段。git merge 不会报冲突(两边改的不是同一行),只有在合并后跑类型检查才暴露。

顺带说明后端侧的现状:CharacterFrame 的持久化结构目前是 { index, image_url, duration_ms }没有 root_motion;带这个字段提交会被 Pydantic 静默丢弃。所以这不只是前端类型对不齐,而是「位移轨这个数据存在哪一层」还没定:

  • 放帧级(Frame.rootMotion)——导出包按帧取位移,符合本 PR 的用法;
  • 放动作级(一条位移轨挂在 action 上)——数据量小,但导出时要自己按帧插值。

无论选哪个,后端 character_data 的形状要跟着改,建议在合并前先把归属定下来(相关契约讨论在 #81)。另外单位口径也要一并定:目前存在「像素」与「角色总高 = 1.0」两种写法,导出包已经在写这个字段了,口径不统一会直接导致播放位移错误。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants